package com.smedi.ismedi.file.core.application.service.Impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.smedi.ismedi.cache.starter.util.RedisUtil;
import com.smedi.ismedi.file.core.application.service.FileApplicationService;
import com.smedi.ismedi.file.core.domain.aggregatemodel.FileInfo;
import com.smedi.ismedi.file.core.domain.service.FileDomainService;
import com.smedi.ismedi.file.core.infrastructure.FileConvert.OpenOfficeUtil;
import com.smedi.ismedi.file.core.infrastructure.FileUtil;
import com.smedi.ismedi.file.core.infrastructure.config.CommonConfig;
import com.smedi.ismedi.file.core.infrastructure.constant.ConstantField;
import com.smedi.ismedi.file.core.infrastructure.constant.CustomError;
import com.smedi.ismedi.file.core.port.adapter.fastdfs.FastDFSClient;
import com.smedi.ismedi.web.starter.exception.LogicException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Author Jimmy
 * @Date 2021/6/1 18:07
 */
@Service
@AllArgsConstructor
@Slf4j
public class FileApplicationServiceImpl implements FileApplicationService {
    private final FileDomainService fileDomainService;

    private final FastDFSClient fastDFSClient;
    private final RedisUtil redisUtil;
    private final CommonConfig commonConfig;

    private final FdfsWebServer fdfsWebServer;

    /**
     * {@inheritDoc}
     */
    @Override
    public FileInfo fileUpLoad(MultipartFile file) {
        StorePath storePath = null;
        try {
            storePath = fastDFSClient.uploadFile(file);
        } catch (Exception ex) {
            throw LogicException.raise(CustomError.FILE_UPLOAD_ERROR);
        }

        String fileExtName = FilenameUtils.getExtension(file.getOriginalFilename());
        if (storePath != null) {
            FileInfo fileInfo = FileInfo.builder()
                    .extName(fileExtName)
                    .name(file.getOriginalFilename())
                    .signKey(IdUtil.simpleUUID())
                    .path(storePath.getPath())
                    .storageGroup(storePath.getGroup())
                    .build();
            FileInfo result = fileDomainService.fileUpLoad(fileInfo);
            if (commonConfig.hasUploadFileConfirm) {
                // 将对象存储到Redis，用于监控，防止有过多作废的文件
                redisUtil.set(ConstantField.profileTempPath + fileInfo.getSignKey(), JSONUtil.toJsonStr(fileInfo), commonConfig.uploadFileConfirmTime);
            }

            return result;
        }
        throw LogicException.raise(CustomError.FILE_UPLOAD_SAVE_ERROR);
    }

    @Override
    public FileInfo fileUpLoad(File file) {
        StorePath storePath;
        try {
            storePath = fastDFSClient.uploadFile(file);
        } catch (Exception ex) {
            throw LogicException.raise(CustomError.FILE_UPLOAD_ERROR);
        }

        String fileExtName = FilenameUtils.getExtension(file.getName());
        if (storePath != null) {
            FileInfo fileInfo = FileInfo.builder()
                    .extName(fileExtName)
                    .name(file.getName())
                    .signKey(IdUtil.simpleUUID())
                    .path(storePath.getPath())
                    .storageGroup(storePath.getGroup())
                    .build();
            FileInfo result = fileDomainService.fileUpLoad(fileInfo);
            if (Boolean.TRUE.equals(commonConfig.hasUploadFileConfirm)) {
                // 将对象存储到Redis，用于监控，防止有过多作废的文件
                redisUtil.set(ConstantField.profileTempPath + fileInfo.getSignKey(), JSONUtil.toJsonStr(fileInfo), commonConfig.uploadFileConfirmTime);
            }

            return result;
        }
        throw LogicException.raise(CustomError.FILE_UPLOAD_SAVE_ERROR);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean uploadFileConfirm(String signKey) {
        if (commonConfig.hasUploadFileConfirm) {
            // 将对象存储到Redis，用于监控，防止有过多作废的文件
            redisUtil.del(ConstantField.profileTempPath + signKey);
        }
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public FileInfo getFileInfo(String signKey) {
        return fileDomainService.getFileInfo(signKey);
    }

    @Override
    public List<FileInfo> getFileInfo(List<String> signKeys) {
        return fileDomainService.getFileInfo(signKeys);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean deleteFileBySignKey(String signKey, String reason) {
        if (commonConfig.isDeleteFileFromFDFS) {
            deleteFileFromFdfs(signKey);
        }
        return fileDomainService.deleteFileBySignKey(signKey, reason);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean downloadFileBySignKey(String signKey, HttpServletResponse response) {
        FileInfo file = fileDomainService.getFileInfo(signKey);
        if (file == null) {
            throw LogicException.raise(CustomError.FILE_NO_FOUND);
        }
        downloadFile(file.getStorageGroup(), file.getPath(), file.getName(), response);
        return true;
    }

    @Override
    public byte[] downloadFileBySignKey(String signKey) throws IOException {
        FileInfo file = fileDomainService.getFileInfo(signKey);
        if (file == null) {
            throw LogicException.raise(CustomError.FILE_NO_FOUND);
        }
        return downloadFile(file.getStorageGroup(), file.getPath(), file.getName());
    }

    @Override
    public void downloadFile(String group, String path, String fileName, HttpServletResponse response) {
        ServletOutputStream outputStream = null;

        try {
            byte[] b = fastDFSClient.downloadFile(group, path);
            response.reset();
            response.setContentType("application/x-download");
            response.addHeader("Content-Disposition", "attachment;filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\"");
            response.getOutputStream().write(b);
            response.getOutputStream().close();
        } catch (IOException e) {
            log.debug("下载文件输出流异常：{}", e);
            throw LogicException.raise(CustomError.FILE_DOWNLOAD_ERROR);
        }
    }

    @Override
    public byte[] downloadFile(String group, String path, String fileName) throws IOException {
        return fastDFSClient.downloadFile(group, path);
    }

    @Override
    public byte[] convertFileToPdfByOpenOffice(MultipartFile file) throws IOException {
        return OpenOfficeUtil.convertToPdf(file);
    }

    @Override
    public byte[] zipBySignKeys(List<String> signKeys) throws IOException {
        File zipFile = new File("D:/zippath.zip");
        List<FileInfo> fileInfos = getFileInfo(signKeys);
        for (FileInfo fileInfo : fileInfos) {
            String filePath = fdfsWebServer.getWebServerUrl() + "/" + fileInfo.getStorageGroup() + "/" + fileInfo.getPath();
            File file = new File(filePath);
            InputStream input = Files.newInputStream(file.toPath());
            ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()));
            zipOut.putNextEntry(new ZipEntry(file.getName()));
            int temp = 0;
            while ((temp = input.read()) != -1) {
                zipOut.write(temp);
            }
            input.close();
            zipOut.close();
        }
        return new byte[0];
    }

    /**
     * 从FDFS删除文件
     *
     * @param signKey
     */
    private void deleteFileFromFdfs(String signKey) {
        FileInfo file = fileDomainService.getFileInfo(signKey);
        if (file == null) {
            throw LogicException.raise(CustomError.FILE_NO_FOUND);
        }
        fastDFSClient.deleteFile(file.getStorageGroup(), file.getPath());
    }

    @Override
    public FileInfo pdfMerge(List<String> signKeys) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        Document document = new Document();
        byte[] pdfs = new byte[0];
        try {
            PdfCopy copy = new PdfCopy(document, bos);
            document.open();
            List<FileInfo> fileInfos = fileDomainService.getFileInfo(signKeys);
            if(CollectionUtils.isEmpty(fileInfos)){
                return null;
            }
            List<FileInfo> fileInfoList = new ArrayList<>();
            for(String signKey :signKeys){
                fileInfoList.addAll(fileInfos.stream().filter(fileInfo -> signKey.equals(fileInfo.getSignKey())).collect(Collectors.toList()));
            }
            for (FileInfo fileInfo : fileInfoList) {
                // 从fdfs下载文件
                byte[] bytes = fastDFSClient.downloadFile(fileInfo.getStorageGroup(), fileInfo.getPath());
                PdfReader reader = new PdfReader(bytes);
                int pageTotal = reader.getNumberOfPages();
                log.info("pdf的页码数是 ==> {}", pageTotal);
                for (int pageNo = 1; pageNo <= pageTotal; pageNo++) {
                    document.newPage();
                    PdfImportedPage page = copy.getImportedPage(reader, pageNo);
                    copy.addPage(page);
                }
                reader.close();
            }
            document.close();
            pdfs = bos.toByteArray();
            bos.close();
            copy.close();
            String tempFileName = UUID.randomUUID().toString();
            File tempFile = File.createTempFile(tempFileName, ".pdf");
            FileUtils.writeByteArrayToFile(tempFile, pdfs);
            MultipartFile multipartFile = FileUtil.fileToMultipartFile(tempFile);
            return this.fileUpLoad(multipartFile);
        } catch (DocumentException e) {
            log.error("合并pdf出错，出错原因：{}", e.getMessage());
        }

        return null;
    }
}
